package com.lin.missyou.logic;

import com.lin.missyou.bo.SkuOrderBO;
import com.lin.missyou.core.enumeration.CouponType;
import com.lin.missyou.core.money.IMoneyDiscout;
import com.lin.missyou.exception.http.ForbiddenException;
import com.lin.missyou.exception.http.ParameterException;
import com.lin.missyou.model.Coupon;
import com.lin.missyou.model.UserCoupon;
import com.lin.missyou.util.CommonUtil;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 优惠卷效验
 *
 * 前端 计算订单的价格
 *      计算的订单的折扣价（最终价）
 * 服务端 计算订单的价格
 *      计算订单的折扣价
 *
 * 单例模式  类似全局变量 默认注入的就是单例的
 * ObjectFactory
 * 动态代理
 * @author gcq
 * @Create 2021-11-09
 */
public class CouponChecker {

    /**
     * 优惠劵
     */
    private Coupon coupon;
    /**
     * 金额折扣
     */
    private IMoneyDiscout imoneyDiscout;

    public CouponChecker(Coupon coupon, IMoneyDiscout iMoneyDiscout)
    {
        this.coupon = coupon;
        this.imoneyDiscout = iMoneyDiscout;
    }

    /**
     * 优惠卷是否过期
     */
    public void isOk()
    {
        Date now = new Date();
        Boolean inTimeLine = CommonUtil.isInTimeLine(now, this.coupon.getStartTime(), this.coupon.getEndTime());
        if(!inTimeLine)
        {
            // 如果优惠卷已过期
            throw new ForbiddenException(40007);
        }
    }

    /**
     * 使用优惠卷后的价格和前端传递过来的优惠卷价格是否一致
     * 价格参数
     *
     * @param orderFinalTotalPrice 订单总金额
     * @param serverTotalPrice 服务端订单计算总金额
     */
    public void finalTotalPriceIsOK(BigDecimal orderFinalTotalPrice,
                                    BigDecimal serverTotalPrice)
    {
        BigDecimal serverFinalTotalPrice;
        switch (CouponType.toType(this.coupon.getType()))
        {
            case FULL_MINUS: // 满减卷
                // 使用优惠卷后，前端传递价格与后端计算价格是否一致
                serverFinalTotalPrice = serverTotalPrice.subtract(this.coupon.getMinus());
                break;
            case FULL_OFF: // 满减折扣
                serverFinalTotalPrice = this.imoneyDiscout.discount(serverTotalPrice, this.coupon.getRate());
                break;
            case NO_THRESHOLD_MINUS: // 无门槛
                serverFinalTotalPrice = serverTotalPrice.subtract(this.coupon.getMinus());
                // 无门槛使用后 金额不能小于0
                if(serverFinalTotalPrice.compareTo(new BigDecimal("0")) < 0)
                {
                    throw new ForbiddenException(50008);
                }
            default:
                throw new ParameterException(40009);
        }
        int compare = serverFinalTotalPrice.compareTo(orderFinalTotalPrice);
        if(compare != 0)
        {
            throw new ForbiddenException(50008);
        }
    }

    /**
     * 优惠卷是否可以使用
     * 带有使用分类限制的优惠卷效验
     * 例如 total 1600
     * 优惠卷限制为 书包 鞋子 那么只有对应分类下的商品达到金额后才能使用
     *
     *
     * 多个参数 定去一个类
     *      BO 业务对象
     * @param  skuOrderBOList 业务bo传输数据
     * @param serverTotalPrice 服务端计算总金额
     */
    public void canBeUsed(List<SkuOrderBO> skuOrderBOList, BigDecimal serverTotalPrice)
    {
        // sku price
        // sku count
        // sku category id
        // coupon category
        BigDecimal orderCategoryPrice;
        if(this.coupon.getWholeStore()) {
            orderCategoryPrice = serverTotalPrice;
        }
        else {
            // 拿到当前优惠券下所有的分类id
            List<Long> cidList = this.coupon.getCategoryList().stream()
                    .map((c) -> c.getId())
                    .collect(Collectors.toList());
            orderCategoryPrice = this.getSumByCategoryList(skuOrderBOList, cidList);
        }
        this.couponCanBeUsed(orderCategoryPrice);
    }

    /**
     * 优惠卷可以使用
     * @param orderCategoryPrice 订单分类总金额
     */
    private void couponCanBeUsed(BigDecimal orderCategoryPrice)
    {
        // 根据优惠券类型 判断是什么类型 进行计算是否合法
        switch (CouponType.toType(this.coupon.getType()))
        {
            case FULL_OFF:
            case FULL_MINUS:
                // 总价格和使用优惠卷后的价格比较
                int compare = this.coupon.getFullMoney().compareTo(orderCategoryPrice);
                // 小于优惠劵后端价格的话 抛出异常
                if(compare < 0)
                {
                    throw new ParameterException(40008);
                }
                break;
            case NO_THRESHOLD_MINUS:
                break;
            default:
                throw new ParameterException(40009);
        }
    }

    /**
     * 获取多个分类的价格
     * @param skuOrderBOList
     * @param cidList 分类Id
     * @return
     */
    private BigDecimal getSumByCategoryList(List<SkuOrderBO> skuOrderBOList, List<Long> cidList)
    {
        BigDecimal sum = cidList.stream()
                .map(cid -> this.getSumByCategory(skuOrderBOList, cid))
                .reduce(BigDecimal::add)
                .orElse(new BigDecimal("0"));
        return sum;
    }

    /**
     * 计算单个分类总价格
     * @param skuOrderBOList bo
     * @param cid 分类id
     * @return
     */
    private BigDecimal getSumByCategory(List<SkuOrderBO> skuOrderBOList, Long cid)
    {
        return skuOrderBOList.stream()
                // 过滤掉其他分类 只匹配与cid对应的
                .filter(sku -> sku.getCategoryId().equals(cid))
                // 拿到当前的总价格
                .map(bo -> bo.getTotalPrice())
                // 计算多个总价格和
                .reduce((bigDecimal, augend) -> bigDecimal.add(augend))
                // 没有的话返回0 因为reduce返回的是optional
                .orElse(new BigDecimal("0"));
    }

}